﻿Ext.Component = Ext.extend(Ext.lib.Component, {});
Ext.reg('component', Ext.Component);


Ext.Component = Ext.extend(Ext.lib.Component, {

    //默认动画效果
    showAnimation: null,

    //是否监听移动设备摆放方向的变化
    monitorOrientation: false,

    //当组件是浮动组件时添加的样式类
    floatingCls: 'x-floating',

    //当组件外部被点击，组件是否隐藏（组件浮动时）
    hideOnMaskTap: true,

    //如果设为true，则当组件外的部分被点击时，阻止事件的响应
    stopMaskTapEvent: true,

    //是否将组件设为居中
    centered: false,

    //是否是模态组件，如果是模态组件，组件外被点击时，点击事件被阻止（组件浮动时）
    modal: false,

    //组件是否占满屏幕
    fullscreen: false,

    //Set this to true to automatically relayout this component on orientation change
    layoutOnOrientationChange: null,


    initComponent: function () {
        this.addEvents(

            'beforeorientationchange',

            'orientationchange'
        );

        //如果组件是全屏或者浮动
        if (this.fullscreen || this.floating) {

            //监听移动设备摆放方向的变化
            this.monitorOrientation = true;

            //自动渲染，自己渲染自己，如果为真则在show方法中才能渲染，
            //这就是为什么组件只有在fullscreen或floating两种状态下才能显示的原因
            this.autoRender = true;
        }

        //如果组件是全屏
        if (this.fullscreen) {
            var viewportSize = Ext.Viewport.getSize();
            this.width = viewportSize.width;//设置宽为屏幕宽
            this.height = viewportSize.height;//设置高为屏幕高
            this.cls = (this.cls || '') + ' x-fullscreen'; //给组件增加x-fullscreen这个类


            // this.renderTo 的定义与否是组件跟抽象组件的分水岭
            this.renderTo = document.body;//目标容器为整个文档
        }
    },

    onRender: function () {

        //父类的同名方法，主要是在目标容器的指定位置上绘制组件
        Ext.Component.superclass.onRender.apply(this, arguments);

        //如果监听移动设备摆放方向的变化
        if (this.monitorOrientation) {
            this.el.addCls('x-' + Ext.Viewport.orientation);//加特殊样式
        }

        //如果是浮动组件
        if (this.floating) {
            this.setFloating(true); //是否将组件设为浮动
        }

        //如果是可拖拽组件
        if (this.draggable) {
            this.setDraggable(this.draggable);//将组件设为可拖拽
        }
        
        //组件的滚动方向，有'horizontal', 'vertical', 'both'三种取值
        if (this.scroll) {
            this.setScrollable(this.scroll);//设置组件的滚动属性
        }
    },

    //渲染后处理
    afterRender: function () {

        //如果是全屏
        if (this.fullscreen) {
            this.layoutOrientation(Ext.Viewport.orientation, this.width, this.height);//该方法未实现
        }

        //调用父类的同名方法，主要实现组件布局定义，调整组件坐标位置、大小，设定读取的页面元素的样式
        Ext.Component.superclass.afterRender.call(this);
    },

    //初始化内部事件
    initEvents: function () {

        //调用父类同名方法，主要实现内部元素事件的延迟注册
        Ext.Component.superclass.initEvents.call(this);

        if (this.monitorOrientation) {
            Ext.EventManager.onOrientationChange(this.setOrientation, this);
        }
    },



    afterComponentLayout: function () {
        var scrollEl = this.scrollEl,
            scroller = this.scroller,
            parentEl;

        if (scrollEl) {
            parentEl = scrollEl.parent();

            if (scroller.horizontal) {
                scrollEl.setStyle('min-width', parentEl.getWidth(true) + 'px');
                scrollEl.setHeight(parentEl.getHeight(true) || null);
            }
            if (scroller.vertical) {
                scrollEl.setStyle('min-height', parentEl.getHeight(true) + 'px');
                scrollEl.setWidth(parentEl.getWidth(true) || null);
            }
            scroller.updateBoundary(true);
        }

        if (this.fullscreen && Ext.is.iPad) {
            Ext.repaint();
        }
    },

    layoutOrientation: Ext.emptyFn,


    update: function () {

        Ext.Component.superclass.update.apply(this, arguments);
        var scroller = this.scroller;
        if (scroller && scroller.updateBoundary) {
            scroller.updateBoundary(true);
        }
    },

    //显示组件
    show: function (animation) {
        var rendered = this.rendered;
        if ((this.hidden || !rendered) && this.fireEvent('beforeshow', this) !== false) {
            
            //如果有锚点子元素
            if (this.anchorEl) {
                this.anchorEl.hide();//隐藏锚点子元素
            }
            //如果组件没有渲染，并且是自动渲染，自己渲染自己
            if (!rendered && this.autoRender) {
                //如果this.autoRender为布尔值true，则将组件绘制到整个页面中，否则绘制到this.autoRender指定的位置
                this.render(Ext.isBoolean(this.autoRender) ? Ext.getBody() : this.autoRender);
            }
            this.hidden = false;

            //如果组件已经渲染
            if (this.rendered) {
                this.onShow(animation);//将组件设为显示
                this.fireEvent('show', this);
            }
        }
        return this;
    },

    //在组件旁边停靠显示，alignTo是目标组件
    showBy: function (alignTo, animation, allowSides, anchor) {

        //如果不是浮动组件，直接返回
        if (!this.floating) {
            return this;
        }

        //获取目标组件的DOM元素
        if (alignTo.isComponent) {
            alignTo = alignTo.el;
        }
        else {
            alignTo = Ext.get(alignTo);
        }

        this.x = 0;
        this.y = 0;

        this.show(animation);//显示组件

        //显示组件锚点子组件
        if (anchor !== false) {
            if (!this.anchorEl) {
                this.anchorEl = this.el.createChild({
                    cls: 'x-anchor'
                });
            }
            this.anchorEl.show();
        }

        //将组件移动到目标组件旁边的适当位置
        this.alignTo(alignTo, allowSides, 20);
    },

    //将组件移动到目标组件旁边的适当位置，alignTo是目标组件
    alignTo: function (alignTo, allowSides, offset) {

        var alignBox = alignTo.getPageBox(),
            constrainBox = {
                width: window.innerWidth,
                height: window.innerHeight
            },
            size = this.getSize(),
            newSize = {
                width: Math.min(size.width, constrainBox.width),
                height: Math.min(size.height, constrainBox.height)
            },
            position,
            index = 2,
            positionMap = [
                'tl-bl',
                't-b',
                'tr-br',
                'l-r',
                'l-r',
                'r-l',
                'bl-tl',
                'b-t',
                'br-tr'
            ],
            anchorEl = this.anchorEl,
            offsets = [0, offset],
            targetBox, cls,
            onSides = false,
            arrowOffsets = [0, 0],
            alignCenterX = alignBox.left + (alignBox.width / 2),
            alignCenterY = alignBox.top + (alignBox.height / 2);

        if (alignCenterX <= constrainBox.width * (1 / 3)) {
            index = 1;
            arrowOffsets[0] = 25;
        }
        else if (alignCenterX >= constrainBox.width * (2 / 3)) {
            index = 3;
            arrowOffsets[0] = -30;
        }

        if (alignCenterY >= constrainBox.height * (2 / 3)) {
            index += 6;
            offsets = [0, -offset];
            arrowOffsets[1] = -10;
        }


        else if (allowSides !== false && alignCenterY >= constrainBox.height * (1 / 3)) {
            index += 3;
            offsets = (index <= 5) ? [offset, 0] : [-offset, 0];
            arrowOffsets = (index <= 5) ? [10, 0] : [-10, 0];
            onSides = true;
        }
        else {
            arrowOffsets[1] = 10;
        }

        position = positionMap[index - 1];
        targetBox = this.el.getAlignToXY(alignTo, position, offsets);



        if (onSides) {
            if (targetBox[0] < 0) {
                newSize.width = alignBox.left - offset;
            }
            else if (targetBox[0] + newSize.width > constrainBox.width) {
                newSize.width = constrainBox.width - alignBox.right - offset;
            }
        }
        else {
            if (targetBox[1] < 0) {
                newSize.height = alignBox.top - offset;
            }
            else if (targetBox[1] + newSize.height > constrainBox.height) {
                newSize.height = constrainBox.height - alignBox.bottom - offset;
            }
        }

        if (newSize.width != size.width) {
            this.setSize(newSize.width);
        }
        else if (newSize.height != size.height) {
            this.setSize(undefined, newSize.height);
        }

        targetBox = this.el.getAlignToXY(alignTo, position, offsets);
        this.setPosition(targetBox[0], targetBox[1]);

        if (anchorEl) {

            anchorEl.removeCls(['x-anchor-bottom', 'x-anchor-left', 'x-anchor-right', 'x-anchor-top']);
            if (offsets[1] == offset) {
                cls = 'x-anchor-top';
            }
            else if (offsets[1] == -offset) {
                cls = 'x-anchor-bottom';
            }
            else if (offsets[0] == offset) {
                cls = 'x-anchor-left';
            }
            else {
                cls = 'x-anchor-right';
            }
            targetBox = anchorEl.getAlignToXY(alignTo, position, arrowOffsets);
            anchorEl.setXY(targetBox);
            anchorEl.addCls(cls);
        }

        return position;
    },

    //设置居中
    setCentered: function (centered, update) {
        this.centered = centered;

        if (this.rendered && update) {
            var x, y;
            if (!this.ownerCt) {
                x = (Ext.Element.getViewportWidth() / 2) - (this.getWidth() / 2);
                y = (Ext.Element.getViewportHeight() / 2) - (this.getHeight() / 2);
            }
            else {
                x = (this.ownerCt.getTargetEl().getWidth() / 2) - (this.getWidth() / 2);
                y = (this.ownerCt.getTargetEl().getHeight() / 2) - (this.getHeight() / 2);
            }
            this.setPosition(x, y);//设置坐标位置
        }

        return this;
    },


    hide: function (animation) {
        if (!this.hidden && this.fireEvent('beforehide', this) !== false) {
            this.hidden = true;
            if (this.rendered) {
                this.onHide(animation, true);
            }
        }
        return this;
    },

    //把组件设为显示
    onShow: function (animation) {

        this.el.show();//显示组件DOM

        //调用父类的同名方法，主要是给组件重新布局，父类的同名方法在父类没有被调
        Ext.Component.superclass.onShow.call(this, animation);

        //设置默认动画
        if (animation === undefined || animation === true) {
            animation = this.showAnimation;
        }

        //如果是浮动组件
        if (this.floating) {

            //如果当前组件的DOM没有父结点，则把组件追加到document.body中
            this.el.dom.parentNode || this.el.appendTo(document.body);

            //如果设置了动画效果
            if (animation) {
                this.el.setStyle('opacity', 0.01);//设置透明度
            }

            //如果设置了居中
            if (this.centered) {
                this.setCentered(true, true);//设置居中
            }
            else {
                //否则按坐标位置放置组件
                this.setPosition(this.x, this.y);
            }

            //如果组件为模态组件
            if (this.modal) {
                //在组件的父容器上加一层透明子组件阻止用户交互
                this.el.parent().mask(null, 'x-mask-gray');
            }

            //如果组件外被点击后隐藏组件，则给文档设置侦听函数onFloatingTouchStart
            if (this.hideOnMaskTap) {
                Ext.getDoc().on('touchstart', this.onFloatingTouchStart, this, { capture: true, subsequent: true });
            }
        }

        //如果设置了动画
        if (animation) {

            //以动画方式显示组件
            Ext.Anim.run(this, animation, {
                out: false,
                autoClear: true
            });

            this.showAnimation = animation;//将默认动画改为当前动画
        }
    },

    //组件外被点击后隐藏组件
    onFloatingTouchStart: function (e) {
        if (!this.el.contains(e.target)) {//如果当前组件不是被点击的组件

            this.hide(); //隐藏当前组件

            //如果设置了阻止掩码元素的事件传递，或被点击的元素包含'x-mask'类
            if (this.stopMaskTapEvent || Ext.fly(e.target).hasCls('x-mask')) {
                e.stopEvent();//阻止事件
            }
        }
    },


    onHide: function (animation, fireHideEvent) {
        if (animation === undefined || animation === true) {
            animation = this.showAnimation;
        }

        if (this.hideOnMaskTap && this.floating) {
            Ext.getDoc().un('touchstart', this.onFloatingTouchStart, this, { capture: true, subsequent: true });
        }

        if (animation) {
            Ext.Anim.run(this, animation, {
                out: true,
                reverse: true,
                autoClear: true,
                scope: this,
                fireHideEvent: fireHideEvent,
                after: this.doHide
            });
        } else {
            this.doHide(null, { fireHideEvent: fireHideEvent });
        }
    },


    doHide: function (el, options) {
        var parent = this.el.parent();

        this.el.hide();

        if (parent && this.floating && this.modal) {
            parent.unmask();
        }
        if (options && options.fireHideEvent) {
            this.fireEvent('hide', this);
        }
    },

    //设置组件的滚动属性
    setScrollable: function (config) {
        var me = this,
            direction;

        //如果组件未渲染，则设置滚动属性后返回
        if (!me.rendered) {
            me.scroll = config;
            return;
        }

        //删除组件原滚动类
        Ext.destroy(me.scroller);
        me.scroller = null;

        
        if (me.originalGetTargetEl) {
            me.getTargetEl = me.originalGetTargetEl;
        }

        //如果设为滚动
        if (config !== false) {
            direction = Ext.isObject(config) ? config.direction : config;
            config = Ext.apply({},
            Ext.isObject(config) ? config : {}, {

                direction: direction
            });

            //如果还没有为组件DOM元素创建滚动的子DOM元素
            if (!me.scrollEl) {
                me.scrollEl = me.getTargetEl().createChild();//创建滚动子DOM元素
            }
            me.originalGetTargetEl = me.getTargetEl;

            //给组件加一个获取滚动子DOM元素的方法
            me.getTargetEl = function () {
                return me.scrollEl;
            };

            //关联滚动类
            me.scroller = (new Ext.util.ScrollView(me.scrollEl, config)).scroller;
        }
    },

    //是否将组件设为浮动
    setFloating: function (floating, autoShow) {
        this.floating = !!floating;
        this.hidden = true;

        //如果组件以及渲染
        if (this.rendered) {
            
            //如果是设为浮动
            if (floating !== false) {
                this.el.addCls(this.floatingCls); //添加浮动样式

                if (autoShow) {//如果是自动显示
                    this.show();
                }
            }
            //否则
            else {
                //移除浮动样式
                this.el.removeCls(this.floatingCls);

                //注销点击外侧就隐藏的函数
                Ext.getDoc().un('touchstart', this.onFloatingTouchStart, this);
            }
        }
        //如果组件没有渲染，且组件设为浮动
        else if (floating !== false) {
            if (this.layoutOnOrientationChange !== false) {
                this.layoutOnOrientationChange = true;
            }
            this.autoRender = true;//将组件设为自动渲染
        }
    },

    //设置组件是否可拖拽
    setDraggable: function (draggable, autoShow) {
        this.isDraggable = draggable;

        //如果组件已经渲染
        if (this.rendered) {
            if (draggable === false) {//如果组件被设为不可拖拽
                
                //且组件本身已经与拖拽类关联
                if (this.dragObj) {
                    this.dragObj.disable();//禁止组件的拖拽属性
                }
            } 
            else {
                if (autoShow) {
                    this.show();//显示组件
                }
                if (this.dragObj) {//如果组件本身已经与拖拽类关联
                    this.dragObj.enable();//启用组件的拖拽属性
                } else {
                    //将组件的DOM元素与拖拽类关联，使其具有拖拽属性
                    this.dragObj = new Ext.util.Draggable(this.el, Ext.apply({}, this.draggable || {}));

                    //继承拖拽类的事件和触发点，即可以为组件注册这些事件的侦听函数
                    this.relayEvents(this.dragObj, ['dragstart', 'beforedragend', 'drag', 'dragend']);
                }
            }
        }
    },


    setOrientation: function (orientation, w, h) {
        if (this.fireEvent('beforeorientationchange', this, orientation, w, h) !== false) {
            if (this.orientation != orientation) {
                this.el.removeCls('x-' + this.orientation);
                this.el.addCls('x-' + orientation);
            }

            this.orientation = orientation;
            this.layoutOrientation(orientation, w, h);

            if (this.fullscreen) {
                this.setSize(w, h);
            }
            else if (this.layoutOnOrientationChange) {
                this.doComponentLayout();
            }

            if (this.floating && this.centered) {
                this.setCentered(true, true);
            }

            this.onOrientationChange(orientation, w, h);
            this.fireEvent('orientationchange', this, orientation, w, h);
        }
    },


    onOrientationChange: Ext.emptyFn,

    //销毁组件前的准备工作
    beforeDestroy: function () {
        if (this.floating && this.modal && !this.hidden) {
            this.el.parent().unmask();//移除掩码元素
        }
        Ext.destroy(this.scroller);//销毁滚动类
        Ext.Component.superclass.beforeDestroy.call(this);
    },

    onDestroy: function () {
        if (this.monitorOrientation && Ext.EventManager.orientationEvent) {
            Ext.EventManager.orientationEvent.removeListener(this.setOrientation, this);
        }

        Ext.Component.superclass.onDestroy.call(this);
    }
});


Ext.BoxComponent = Ext.Component;

Ext.reg('component', Ext.Component);
Ext.reg('box', Ext.BoxComponent);